package org.infodavid.common.model.impl;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.infodavid.common.model.IDocument;
import org.infodavid.common.model.IDocumentCategory;
import org.infodavid.common.model.IDocumentVersion;
import org.infodavid.common.model.IGenericPropertiesMap;
import org.infodavid.common.model.IUser;
import org.infodavid.common.model.ModelFactory;

/**
 * The Class DocumentVersionImpl.
 */
public class DocumentVersionImpl extends AbstractVersionedObject<Long> implements IDocumentVersion {

	/** serialVersionUID. */
	private static final long serialVersionUID = 6523279718688638431L;

	/** The approval status. */
	private EApprovalStatus approvalStatus;

	/** The attachments. */
	private Map<String,IDocument> attachments;

	/** The category. */
	private IDocumentCategory category;

	/** The comment. */
	private String comment;

	/** The data. */
	private byte[] data;

	/** The document. */
	private IDocument document;

	/** The encoding. */
	private String encoding;

	/** The language. */
	private String language;

	/** The properties. */
	private IGenericPropertiesMap properties;

	/** The title. */
	private String title;

	/** The owner details. */
	private IUser user;

	/**
	 * constructor.
	 * @throws Exception the exception
	 */
	protected DocumentVersionImpl() throws Exception {
		super();
	}

	/**
	 * constructor.
	 * @param keyArg the key
	 * @throws Exception the exception
	 */
	protected DocumentVersionImpl(final Long keyArg) throws Exception {
		this();

		setKey(keyArg);
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#addAttachment(org.infodavid.common.model.
	 * IDocumentVersion )
	 */
	@Override
	public void addAttachment(final IDocument value) {
		getAttachments().put(value.getName(), value);
	}

	/**
	 * Check document.
	 */
	private void checkDocument() {
		synchronized (this) {
			if (document == null) {
				try {
					document = ModelFactory.getInstance().create(IDocument.class);
				}
				catch (final InstantiationException e) {
					throw new Error(e);
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IApprovable#getApprovalStatus()
	 */
	@Override
	public EApprovalStatus getApprovalStatus() {
		return approvalStatus;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getAttachments()
	 */
	@Override
	public Map<String,IDocument> getAttachments() {
		return attachments;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getCategory()
	 */
	@Override
	public IDocumentCategory getCategory() {
		return category;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.ICommented#getComment()
	 */
	@Override
	public String getComment() {
		return comment;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getData()
	 */
	@Override
	public byte[] getData() {
		return data;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getDocument()
	 */
	@Override
	public IDocument getDocument() {
		return document;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getEncoding()
	 */
	@Override
	public String getEncoding() {
		return encoding;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getLanguage()
	 */
	@Override
	public String getLanguage() {
		return language;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getMimeType()
	 */
	/**
	 * Gets the mime type.
	 * @return the mime type
	 */
	public String getMimeType() {
		return getDocument() == null ? null : getDocument().getMimeType();
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.INamed#getName()
	 */
	@Override
	public String getName() {
		return getDocument() == null ? null : getDocument().getName();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.data.IGenericObject#getProperties()
	 */
	@Override
	public IGenericPropertiesMap getProperties() {
		return properties;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getTitle()
	 */
	@Override
	public String getTitle() {
		return title;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#getUser()
	 */
	@Override
	public IUser getUser() {
		return user;
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#init()
	 */
	@Override
	public void init() throws Exception {
		super.init();

		comment = null;
		data = null;
		title = null;
		category = null;
		language = null;
		encoding = null;
		user = null;
		approvalStatus = EApprovalStatus.NONE;
		attachments = new HashMap<String,IDocument>();
		properties = ModelFactory.getInstance().create(IGenericPropertiesMap.class);
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#removeAttachment(org.infodavid.common.model.
	 * IDocumentVersion)
	 */
	@Override
	public void removeAttachment(final IDocument value) {
		getAttachments().remove(value.getName());
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * org.infodavid.common.model.IApprovable#setApprovalStatus(org.infodavid.common.model.IApprovable
	 * .EApprovalStatus)
	 */
	@Override
	public void setApprovalStatus(final EApprovalStatus value) {
		if (!equals(approvalStatus, value)) {
			final Object old = approvalStatus;
			approvalStatus = value;
			markModified(APPROVAL_STATUS, old, approvalStatus);
		}
	}

	/**
	 * Sets the attachments.
	 * @param values the attachments
	 */
	protected void setAttachments(final Map<String,IDocument> values) {
		final Object old = attachments;
		attachments = values;
		markModified(ATTACHMENTS, old, attachments);
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see
	 * org.infodavid.common.model.IDocumentVersion#setCategory(org.infodavid.common.model.ICategory)
	 */
	@Override
	public void setCategory(final IDocumentCategory value) {
		if (!equals(category, value)) {
			final Object old = category;
			category = value;
			markModified(CATEGORY, old, category);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.ICommented#setComment(java.lang.String)
	 */
	@Override
	public void setComment(final String value) {
		if (!equals(comment, value)) {
			final Object old = comment;
			comment = value;
			markModified(COMMENT, old, comment);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#setData(byte[])
	 */
	@Override
	public void setData(final byte[] values) {
		if (!equals(data, values)) {
			final Object old = data;
			data = values;
			markModified(DATA, old, data);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see
	 * org.infodavid.common.model.IDocumentVersion#setDocument(org.infodavid.common.model.IDocument)
	 */
	@Override
	public void setDocument(final IDocument value) {
		if (!equals(document, value)) {
			final Object old = document;
			document = value;
			markModified(IDocument.DOCUMENT, old, document);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#setEncoding(java.lang.String)
	 */
	@Override
	public void setEncoding(final String value) {
		if (!equals(encoding, value)) {
			final Object old = encoding;
			encoding = value;
			markModified(ENCODING, old, encoding);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#setLanguage(java.lang.String)
	 */
	@Override
	public void setLanguage(final String value) {
		if (!equals(language, value)) {
			final Object old = language;
			language = value;
			markModified(LANGUAGE, old, language);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#setMimeType(java.lang.String)
	 */
	/**
	 * Sets the mime type.
	 * @param value the new mime type
	 */
	public void setMimeType(final String value) {
		checkDocument();

		getDocument().setMimeType(value);
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.INamed#setName(java.lang.String)
	 */
	@Override
	public void setName(final String arg0) {
		checkDocument();

		getDocument().setName(arg0);
	}

	/**
	 * Sets the properties.
	 * @param values the properties
	 */
	protected void setProperties(final IGenericPropertiesMap values) {
		final Object old = properties;
		properties = values;
		markModified(PROPERTIES, old, properties);
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#setTitle(java.lang.String)
	 */
	@Override
	public void setTitle(final String value) {
		if (!equals(title, value)) {
			final Object old = title;
			title = value;
			markModified(TITLE, old, title);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.IDocumentVersion#setUser(org.infodavid.common.model.IUser)
	 */
	@Override
	public void setUser(final IUser value) {
		if (!equals(user, value)) {
			final Object old = user;
			user = value;
			markModified(IUser.USER, old, user);
		}
	}

	/*
	 * See super class or interface. (non-Javadoc)
	 * @see org.infodavid.common.impl.model.DataObjectImpl#toString(java.lang.StringBuffer)
	 */
	@Override
	protected StringBuffer toString(final StringBuffer builder) {
		super.toString(builder);

		builder.append("[comment=");
		builder.append(comment);
		builder.append(", document=");

		if (document == null) {
			builder.append("null");
		}
		else {
			builder.append(document.getKey() + " (" + document.getName() + ')');
		}

		builder.append(", category=");

		if (category == null) {
			builder.append("null");
		}
		else {
			builder.append(category.getKey());
		}

		builder.append(", title=");
		builder.append(title);
		builder.append(", user=");

		if (user == null) {
			builder.append("null");
		}
		else {
			builder.append(user.getKey());
		}

		builder.append(", approvalStatus=");
		builder.append(approvalStatus);
		builder.append(", language=");
		builder.append(language);
		builder.append(", encoding=");
		builder.append(encoding);
		builder.append(", properties=");
		builder.append(properties);
		builder.append(", attachments=");

		if (attachments == null) {
			builder.append("null");
		}
		else {
			builder.append(Arrays.toString(attachments.keySet().toArray()));
		}

		builder.append(", data=");

		if (data == null) {
			builder.append("null");
		}
		else {
			builder.append(data.length);
			builder.append(" bytes");
		}

		builder.append(']');

		return builder;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.impl.DataObjectImpl#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;

		result = prime * result + (getDocument() == null ? 0 : getDocument().hashCode());

		return prime * result + getRevision();
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.model.impl.DataObjectImpl#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}

		if (obj == null) {
			return false;
		}

		if (!(obj instanceof IDocumentVersion)) {
			return false;
		}

		final IDocumentVersion other = (IDocumentVersion)obj;

		if (getDocument() == null) {
			if (other.getDocument() != null) {
				return false;
			}
		}
		else if (!getDocument().equals(other.getDocument())) {
			return false;
		}

		if (getRevision() != other.getRevision()) {
			return false;
		}

		return true;
	}
}
